/*
*  Copyright 2019-2020 Zheng Jie
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package me.zhengjie.modules.system.service.impl;

import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import me.zhengjie.constant.ConstantUtils;
import me.zhengjie.modules.system.domain.*;
import me.zhengjie.modules.system.domain.vo.LastMonthNewUserVo;
import me.zhengjie.modules.system.domain.vo.WxUserStatisticsVo;
import me.zhengjie.modules.system.service.mapstruct.WxUserExpMapper;
import me.zhengjie.modules.system.service.mapstruct.WxUserMapper;
import me.zhengjie.modules.web.service.mybatisplus.ICourseInfoPlusService;
import me.zhengjie.modules.web.service.mybatisplus.IDisUserRightPlusService;
import me.zhengjie.modules.web.service.mybatisplus.IWxUserPlusService;
import me.zhengjie.modules.weixin.utils.DateUtil;
import me.zhengjie.utils.*;
import lombok.RequiredArgsConstructor;
import me.zhengjie.modules.system.repository.WxUserRepository;
import me.zhengjie.modules.system.service.WxUserService;
import me.zhengjie.modules.system.service.dto.WxUserDto;
import me.zhengjie.modules.system.service.dto.WxUserQueryCriteria;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.util.CollectionUtils;

import java.sql.Timestamp;
import java.util.*;
import java.io.IOException;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.stream.Collectors;

/**
 * @author zhaoxiancheng
 * @website https://el-admin.vip
 * @description 服务实现
 * @date 2021-11-19
 **/
@Service
@RequiredArgsConstructor
public class WxUserServiceImpl implements WxUserService {

    private final WxUserRepository wxUserRepository;
    private final WxUserMapper wxUserMapper;

    @Autowired
    WxUserExpMapper wxUserExpMapper;

    @Autowired
    private IWxUserPlusService wxUserPlusService;

    @Autowired
    private ICourseInfoPlusService courseInfoPlusService;

    @Autowired
    private IDisUserRightPlusService userRightPlusService;

    @PersistenceContext
    private EntityManager em;

    private static final Logger log = LoggerFactory.getLogger(WxUserServiceImpl.class);

    @Override
    public Map<String, Object> queryAll(WxUserQueryCriteria criteria, Pageable pageable) {
        Page<WxUser> page = wxUserRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(wxUserMapper::toDto));
    }

    @Override
    public Map<String, Object> queryDisMemberAll(QueryDisMemberRequest queryDisMemberRequest, Pageable pageable) {
        WxUserQueryCriteria criteria = new WxUserQueryCriteria();
        criteria.setNickname(queryDisMemberRequest.getNickname());
        criteria.setRealName(queryDisMemberRequest.getRealName());
        criteria.setMobile(queryDisMemberRequest.getMobile());
        criteria.setSuperiorMobile(queryDisMemberRequest.getSuperiorMobile());
        criteria.setSuperiorNickName(queryDisMemberRequest.getSuperiorNickName());
        Timestamp now = me.zhengjie.utils.DateUtil.TimestampNow();
        if (queryDisMemberRequest.isDormant()){
            criteria.setLockTime(now);
        }
        criteria.setIdentityNo(queryDisMemberRequest.getIdentityNo());
        criteria.setPartnerId(queryDisMemberRequest.getPartnerId());

        Page<WxUser> page = wxUserRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        Map<String, Object> resultMap = PageUtil.toPage(page.map(wxUserMapper::toDto));
        if (MapUtil.isEmpty(resultMap)){
            return null;
        }
        for (WxUserDto obj : (List<WxUserDto>)resultMap.get("content")) {
            if(StringUtils.isBlank(obj.getHeadimgurl()))
            {
                obj.setHeadimgurl("http://wx.drzh-atmr.cn/static/images/head.jpg");
            }
            if (Objects.nonNull(obj.getLockTime()) && obj.getLockTime().before(now)){
                obj.setDormant(true);
            }
            LambdaQueryWrapper<DisUserRight> rightWrapper = new LambdaQueryWrapper<>();
            String openId = obj.getOpenId();
            rightWrapper.eq(DisUserRight::getOpenId, openId);
            rightWrapper.eq(DisUserRight::getRightStatus, ConstantUtils.Valid);
            List<DisUserRight> list = userRightPlusService.list(rightWrapper);
            if (!CollectionUtils.isEmpty(list)){
                List<String> courseNoList = list.parallelStream().map(DisUserRight::getRightContent).collect(Collectors.toList());
                LambdaQueryWrapper<CourseInfo> courseWrapper = new LambdaQueryWrapper<>();
                courseWrapper.in(CourseInfo::getCourseNo,courseNoList);
                List<CourseInfo> courseInfoList = courseInfoPlusService.list(courseWrapper);
                obj.setCourseRightList(courseInfoList);
            }
            //查询用户链条
            List<WxUserDto.UserChain> userChain = getUserChain(openId);
            obj.setUserChain(userChain);
        }
        return resultMap;
    }

    @Override
    public List<WxUserDto> queryAll(WxUserQueryCriteria criteria) {
        return wxUserMapper.toDto(wxUserRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    public List<WxUserDto> queryAllUser(WxUserQueryCriteria criteria) {
        //return wxUserMapper.toDto(wxUserRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)));
        return wxUserExpMapper.queryAllUser();
    }

    @Override
    @Transactional
    public WxUserDto findById(Integer id) {
        WxUser wxUser = wxUserRepository.findById(id).orElseGet(WxUser::new);
        ValidationUtil.isNull(wxUser.getId(), "WxUser", "id", id);
        return wxUserMapper.toDto(wxUser);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WxUserDto create(WxUser resources) {
        return wxUserMapper.toDto(wxUserRepository.save(resources));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(WxUser resources) {
        WxUser wxUser = wxUserRepository.findById(resources.getId()).orElseGet(WxUser::new);
        ValidationUtil.isNull(wxUser.getId(), "WxUser", "id", resources.getId());
        wxUser.copy(resources);
        wxUserRepository.save(wxUser);
    }

    @Override
    public void deleteAll(Integer[] ids) {
        for (Integer id : ids) {
            wxUserRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<WxUserDto> all, HttpServletResponse response) throws IOException {
        List<List> dataList = new ArrayList<List>();
        for (WxUserDto wxUser : all) {
            List<Object> data = new ArrayList<>();
            data.add(wxUser.getOpenId());
            data.add(wxUser.getNickname());
            data.add(wxUser.getCity());
            data.add(StringUtils.isNotEmpty(wxUser.getSex()) ? wxUser.getSex().equals("1") ? "男" : "女" : "男");
            data.add(wxUser.getMobile());
            data.add(wxUser.getR2time());
            data.add(StringUtils.isNotEmpty(wxUser.getR2result()) ? wxUser.getR2result().replaceAll("\\.0", "") : "");
            data.add(wxUser.getR5time());
            data.add(StringUtils.isNotEmpty(wxUser.getR5result()) ? wxUser.getR5result().replaceAll("\\.0", "") : "");
            dataList.add(data);
        }
        List<String> titles = new ArrayList<String>();
        titles.add("序号"); // 1
        titles.add("OpenID"); // 2
        titles.add("微信昵称"); // 3
        titles.add("姓名"); // 4
        titles.add("性别"); // 5
        titles.add("联系电话"); // 6
        titles.add("最新一次极速版测试时间"); // 7
        titles.add("最新一次极速版结果"); // 8
        titles.add("最新一次专业版测试时间"); // 9
        titles.add("最新一次专业版结果"); // 10

        FileUtil.uploadExcelAboutUser(response, "WX_user.xls", titles, dataList);
    }

    @Override
    public WxUser findByOpenId(String openId) {
        if(StringUtils.isBlank(openId)){
            return null;
        }
        WxUserQueryCriteria criteria = new WxUserQueryCriteria();
        criteria.setOpenId(openId);
        List<WxUser> list = wxUserRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder));
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        return list.get(0);
    }

    /**
     * 统计用户
     *
     * @return
     */
    @Override
    public WxUserStatisticsVo count() {
        Date now = new Date();
        WxUserStatisticsVo userStatisticsVo = new WxUserStatisticsVo();
        WxUserQueryCriteria criteria = new WxUserQueryCriteria();
        criteria.setSubscribeTimeFrom(DateUtil.date2String(DateUtil.getDayStartDate(now), DateUtil.DATE_PATTERN_YYYY_MM_DD_HH_MM_SS));
        long today = wxUserRepository.count((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder));
        userStatisticsVo.setTodayNewUserCount(today);
        userStatisticsVo.setUserCount(wxUserRepository.count());
        List<LastMonthNewUserVo> list = wxUserRepository.countLastMonthUser(DateUtil.date2String(DateUtil.getMonthStartDate(now), DateUtil.DATE_PATTERN_YYYY_MM_DD_HH_MM_SS), DateUtil.date2String(DateUtil.getMonthEndDate(now)
                , DateUtil.DATE_PATTERN_YYYY_MM_DD_HH_MM_SS));
        userStatisticsVo.setLastMonthNewUserList(list);
        return userStatisticsVo;
    }

    @Override
    public List<WxUser> findByMoblie(String moblie) {
        return wxUserRepository.findByMoblie(moblie);
    }


    @Override
    public List<WxUser> findByCity(String city) {
        return wxUserRepository.findByCity(city);
    }


    @Override
    public List<WxUser> findByParam(String city, String moblie) {
        return wxUserRepository.findByParam(city, moblie);
    }

    @Override
    public boolean setUserPartner(String openId, DisPartner disPartner) {
        if (StringUtils.isBlank(openId) || Objects.isNull(disPartner)){
            return false;
        }
        WxUser wxUser = new WxUser();
        wxUser.setPartnerId(disPartner.getPartnerId());
        wxUser.setPartnerName(disPartner.getPartnerName());
        LambdaQueryWrapper<WxUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WxUser::getOpenId,openId);
        return wxUserPlusService.update(wxUser,wrapper);
    }

    @Override
    public boolean batchSetUserPartner(List<String> openIdList, DisPartner disPartner) {
        if (CollectionUtils.isEmpty(openIdList) || Objects.isNull(disPartner)){
            return false;
        }
        WxUser wxUser = new WxUser();
        wxUser.setPartnerId(disPartner.getPartnerId());
        wxUser.setPartnerName(disPartner.getPartnerName());
        LambdaQueryWrapper<WxUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(WxUser::getOpenId,openIdList);
        return wxUserPlusService.update(wxUser,wrapper);
    }

    @Override
    public List<WxUserDto.UserChain> getUserChain(String openId) {
        //查询上级
        List<WxUserDto.UserChain> userChainList = new ArrayList<>();
        if (StringUtils.isBlank(openId)){
            log.info("getUserChain openId is null");
            return userChainList;
        }
        WxUser one = getPreWxUser(openId);
        if (Objects.isNull(one)){
            log.info("getUserChain getPreWxUser is null");
            return userChainList;
        }
        String superior = one.getSuperior();
        //有上级
        if (StringUtils.isNotBlank(superior)){
            WxUser wxUser = getPreWxUser(superior);
            WxUserDto.UserChain preChain = new WxUserDto.UserChain();
            preChain.setLevel(0);
            preChain.setRealName(wxUser.getRealName());
            preChain.setMobile(wxUser.getMobile());
            preChain.setOpenId(wxUser.getOpenId());
            preChain.setNickName(wxUser.getNickname());
            userChainList.add(preChain);
        }
        //查询下级
        int level = 1;
        List<WxUser> nextWxUser = getNextWxUser(openId);
        if (CollectionUtils.isEmpty(nextWxUser)){
            log.info("getUserChain getNextWxUser is null,userChainList="+ JSON.toJSONString(userChainList));
            return userChainList;
        }
            //下一级openId集合
        List<String> OpenIdList = nextWxUser.parallelStream().map(WxUser::getOpenId).collect(Collectors.toList());
        nextWxUser.forEach(wxUser -> {
            WxUserDto.UserChain nextChain = new WxUserDto.UserChain();
            nextChain.setLevel(level);
            nextChain.setRealName(wxUser.getRealName());
            nextChain.setMobile(wxUser.getMobile());
            nextChain.setOpenId(wxUser.getOpenId());
            nextChain.setNickName(wxUser.getNickname());
            userChainList.add(nextChain);
        });
        log.info("getUserChain userChainList1="+ JSON.toJSONString(userChainList));
        recursionFunction(OpenIdList,level,userChainList);
        log.info("getUserChain userChainList2="+ JSON.toJSONString(userChainList));
        return userChainList;
    }

    @Override
    public Map<String, Object> getCurrentUserChain(GetCurrentUserChainRequest request, Pageable pageable) {
        Map<String, Object> finalMap = new HashMap<String,Object>(){{
            put("content",new ArrayList<>());
            put("totalElements",0);
        }};
        String currentUserMobile = SecurityUtils.getCurrentUserMobile();
        List<WxUser> byMoblie = findByMoblie(currentUserMobile);
        if (CollectionUtils.isEmpty(byMoblie)){
            return finalMap;
        }
        WxUser wxUser = byMoblie.get(0);
        List<WxUserDto.UserChain> userChain = getUserChain(wxUser.getOpenId());
        finalMap.put("content",userChain);
        finalMap.put("totalElements",userChain.size());
        return finalMap;
    }

    private List<WxUser> recursionFunction(List<String> openIdList, int level, List<WxUserDto.UserChain> list){
        if (CollectionUtils.isEmpty(openIdList)){
            return null;
        }
        List<WxUser> nextWxUserList = getNextWxUserList(openIdList);
        if (!CollectionUtils.isEmpty(nextWxUserList)){
            level ++ ;
            for (WxUser wxUser : nextWxUserList) {
                WxUserDto.UserChain nextChain = new WxUserDto.UserChain();
                nextChain.setLevel(level);
                nextChain.setRealName(wxUser.getRealName());
                nextChain.setMobile(wxUser.getMobile());
                nextChain.setOpenId(wxUser.getOpenId());
                nextChain.setNickName(wxUser.getNickname());
                list.add(nextChain);
            }
            List<String> collect = nextWxUserList.parallelStream().map(WxUser::getOpenId).collect(Collectors.toList());
            return recursionFunction(collect,level,list);
        }
        return null;
    }
    private WxUser getPreWxUser(String openId){
        LambdaQueryWrapper<WxUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WxUser::getOpenId,openId);
        WxUser one = wxUserPlusService.getOne(wrapper);
        return one;
    }

    private List<WxUser> getNextWxUser(String openId){
        LambdaQueryWrapper<WxUser> nextWrapper = new LambdaQueryWrapper<>();
        nextWrapper.in(WxUser::getSuperior,openId);
        return wxUserPlusService.list(nextWrapper);
    }
    private List<WxUser> getNextWxUserList(List<String> openId){
        LambdaQueryWrapper<WxUser> nextWrapper = new LambdaQueryWrapper<>();
        nextWrapper.in(WxUser::getSuperior,openId);
        return wxUserPlusService.list(nextWrapper);
    }
}